package com.waimai.app.qa.listener;

import java.util.ArrayList;
import java.util.List;

import org.testng.IResultMap;
import org.testng.ITestContext;
import org.testng.ITestNGMethod;
import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.internal.ResultMap;

import com.waimai.app.qa.util.Logger;

/**
 * TestNGListener，来处理TestCase Retry的状态。在onTestFailure方法里，未达到最大retry次数的失败Case，我们把它的状态设置为SKIP，这样TestNG不会把它统计为Failed的test case。 
 * @author aihui
 * http://shijunjuan.iteye.com/blog/1678735
 */
public class RetryTestListener extends Listener {
	
	private IResultMap failedCases = new ResultMap();
	private IResultMap skippedCases = new ResultMap();
	private boolean isRetryHandleNeeded;
	
	public synchronized void onTestFailure(ITestResult arg0) {
			
		if (arg0.getMethod().getRetryAnalyzer() != null) {
			RetryTestAnalyzer retryTestAnalyzer = (RetryTestAnalyzer) arg0.getMethod().getRetryAnalyzer();
			
			if (retryTestAnalyzer.getCount() <= retryTestAnalyzer.getMaxCount()) {
				arg0.setStatus(ITestResult.SKIP);
				Reporter.setCurrentTestResult(null);
			}
			else{
				failedCases.addResult(arg0, arg0.getMethod());
				isRetryHandleNeeded = true;
			}
		}
	}

	/* 
	 * 在OnFinish方法里面处理最后TestCase的Result。
	 * (non-Javadoc)
	 * @see org.testng.TestListenerAdapter#onFinish(org.testng.ITestContext)
	 */
	public void onFinish(final ITestContext arg0) {
	
		if(isRetryHandleNeeded){
			removeIncorrectlySkippedTests(arg0,failedCases);
			removeFailedTestsInTestNG(arg0);
		}
		else{
			skippedCases = arg0.getSkippedTests();
			failedCases = arg0.getFailedTests();
		}
	}

	/**
	 * TestNG可能会把TestCase同时放在Skipped和Failed里面，所以我们在Skipped的testcase里面剔除真正Failed的Test Case（最后一次Retry失败的Case） 
	 * @param test
	 * @param map
	 * @return
	 */
	protected IResultMap removeIncorrectlySkippedTests(ITestContext test,IResultMap map){   
		List<ITestNGMethod> failsToRemove = new ArrayList<ITestNGMethod>();
		IResultMap returnValue = test.getSkippedTests();

		for(ITestResult result : returnValue.getAllResults())
		{
			for(ITestResult resultToCheck : map.getAllResults())
			{
				if(resultToCheck.getMethod().equals(result.getMethod()))
				{
					failsToRemove.add(resultToCheck.getMethod());
					break;
				}
			}  
			for(ITestResult resultToCheck : test.getPassedTests().getAllResults())
			{
				if(resultToCheck.getMethod().equals(result.getMethod()))
				{
					failsToRemove.add(resultToCheck.getMethod());
					break;
				}
			}   
		}
		for(ITestNGMethod method : failsToRemove){
			returnValue.removeResult(method);
		}
		skippedCases = returnValue;
		return returnValue;
	}

	private void removeFailedTestsInTestNG(ITestContext test)
	{   
		IResultMap returnValue = test.getFailedTests();
		for(ITestResult result : returnValue.getAllResults()){
			
			boolean isFailed = false;
			for(ITestResult resultToCheck : failedCases.getAllResults()){
				if(result.getMethod().equals(resultToCheck.getMethod())){
					Logger.Defaultlog("Passed:"+result.getMethod().getMethodName());
					isFailed = true;
					break;
				}
			}
			if(!isFailed){
				returnValue.removeResult(result.getMethod());
				test.getFailedConfigurations().removeResult(result.getMethod());
			}
		}
	}
} 